
                /*********************************************
                **                                          **
                **                                          **
                **                 cs.5.C                   **
                **                                          **
                **                                          **
                *********************************************/
                /*********************************************
                **                                          **
                **         Author Jean-Marc Adamo           **
                **       Copyright (C) 1997-2008 JMA        **
                *********************************************/


/*
Synchronous Communication
This program is to be run on two processors or more. 
On each processor, the Root_thread instance spawns three 
concurrent s_threads: sthread1, sthread2 and sync. The 
first two s_threads respectively communicate and 
synchronize with remote left and right sync. Next, sthread1 
and sthread2 respectively spawn two s_threads that 
locally communicate with sync. Communication is performed 
through inter-processor and local synchronous channels. 
Sync makes use of the alt function that allows it to 
synchronize with each connected s_thread 
individually and prevents from any more global 
synchronization.
*/

#include "arch.h"

//user program
Global_id gid1;
Global_id gid2;
Coll_id cid;

void init_gid(int, char **){
   gid1.set();
   gid2.set();
   cid.set();
   fprintf(Sdf::trace, "node%d: gid1.tag=%d, gid2.tag=%d, cid.tag=%d\n",
           Sdf::PROC_ID, gid1.get(), gid2.get(), cid.get());
   fflush(Sdf::trace);
}

#define B 5

ChanLD<int> *chan[4];

class Id{
   public:
        int processor_Id;
        int process_Id;
};

ChanRD<Id> *proc_to_left;
ChanRD<Id> *proc_to_right;


class Proc: public S_Thread{

   public:
        int pid;
        int step;
        Id id;

        Proc(int p, int s): S_Thread((Smf0_ptr)&Proc::body){
                pid = p;
                step = s;
                id.processor_Id = Sdf::PROC_ID;
                id.process_Id   = pid;
        }

        void body(){

                fprintf(Sdf::trace, "node %d, in proc %d before loop at step %d\n",
                        Sdf::PROC_ID, pid, step);
                fflush(Sdf::trace);
                int i;
                for(i = 0; i < B; i++){
                   fprintf(Sdf::trace, "node %d, proc %d in for loop, i = %d\n",
                           Sdf::PROC_ID, pid, i);
                   fflush(Sdf::trace);
                   reschedule();
                }

                fprintf(Sdf::trace, "node %d, in proc %d after for loop at step %d\n",
                        Sdf::PROC_ID, pid, step);
                fflush(Sdf::trace);

                if(step==0 && pid==1 && Sdf::PROC_ID>0){
                       proc_to_left->send(id);
                       fprintf(Sdf::trace, "node %d, in proc %d, after send left, at step %d\n",
                               Sdf::PROC_ID, pid, step);
                       fflush(Sdf::trace);
                }

                if(step==0 && pid==2 && Sdf::PROC_ID<Sdf::PROC_NB-1){
                   proc_to_right->send(id);
                   fprintf(Sdf::trace, "node %d, in proc %d after send right at step %d\n",
                           Sdf::PROC_ID, pid, step);
                   fflush(Sdf::trace);
                }
                int index;
/*
                union S_or_T{
                   Thread  *T;
                   Proc    *S;
                } f_ptr;
                f_ptr.T = father_ptr;
                if(step==1){
                   if( (f_ptr.S)->pid == 1 )
                      index = pid%10 - 1;
                   else
                      index = pid%20 + 1;
                   chan[index]->send(pid);
                   fprintf(Sdf::trace, "node %d, in proc %d after send at step %d, index = %d, data sent: %d\n", 
                           Sdf::PROC_ID, pid, step, index, pid);
                   fflush(Sdf::trace);
                }
*/

                Proc *f_ptr = (Proc *)father_ptr;
                if(step==1){
                   if( f_ptr->pid == 1 )
                      index = pid%10 - 1;
                   else
                      index = pid%20 + 1;
                   chan[index]->send(pid);
                   fprintf(Sdf::trace, "node%d, sthread%d after send,step %d,index=%d,data sent:%d\n",
                                Sdf::PROC_ID, pid, step, index, pid);
                   fflush(Sdf::trace);
                }

                if(step<1){
                   Proc *proc1 = new Proc(pid*10 + 1, step+1);
                   Proc *proc2 = new Proc(pid*10 + 2, step+1);
                   S_Thread *proc_tabl[2] = {proc1, proc2};
                   int pri[2] = {1, 1};
                   par(2, proc_tabl, pri);
                   delete proc1, delete proc2;
                }

                fprintf(Sdf::trace, "node %d, proc %d the end at step %d\n",
                        Sdf::PROC_ID, pid, step);
                fflush(Sdf::trace);
        }
};

class Sync: public S_Thread{

   public:
        int pid[4];
        Id id_left, id_right;

        Sync()
           : S_Thread((Smf0_ptr)&Sync::body){}

        void body(){
                fprintf(Sdf::trace, "node %d, in Sync::body1\n", Sdf::PROC_ID);
                fflush(Sdf::trace);
                Alts *alt_table2[2];
                if(Sdf::PROC_ID > 0 && Sdf::PROC_ID < Sdf::PROC_NB-1){
                    alt_table2[0] =
                        new AltRDrecv<Id>(proc_to_left,
                                          this,
                                          (Smf2_ptr)&Sync::alt_body2,
                                          0,
                                          id_left);
                    alt_table2[1] =
                          new AltRDrecv<Id>(proc_to_right,
                                            this,
                                            (Smf2_ptr)&Sync::alt_body2,
                                            0,
                                            id_right);

                   AltCtrl *alt_ctrl2 = new AltCtrl(2, alt_table2);

                   for(int i = 0; i< 2; i++){
                      Sdf::alt(alt_ctrl2);
                   }

                   delete alt_ctrl2;
                   delete alt_table2[1]; delete alt_table2[0];

                   fprintf(Sdf::trace, "node %d, sync after for 2\n", Sdf::PROC_ID);
                   fflush(Sdf::trace);
                }
                if (Sdf::PROC_ID == 0){

                   proc_to_left->recv(id_left);

                   fprintf(Sdf::trace, "node %d, sync after recv from proc_to_left, id_left.processor_Id = %d, id_left.process_Id = %d\n",
                           Sdf::PROC_ID, id_left.processor_Id, id_left.process_Id);
                   fflush(Sdf::trace);
                }
                if (Sdf::PROC_ID == Sdf::PROC_NB-1){

                   proc_to_right->recv(id_right);

                   fprintf(Sdf::trace,"node %d, sync after recv from proc_to_right, id_right.processor_Id = %d, id_right.process_Id = %d\n",
                           Sdf::PROC_ID, id_right.processor_Id, id_right.process_Id);
                   fflush(Sdf::trace);
                }

                Alts *alt_table4[4];
                alt_table4[0] =
                        new AltLDrecv<int>(chan[0],
                                           this,
                                           (Smf2_ptr)&Sync::alt_body4,
                                           0,
                                           pid[0]);
                alt_table4[1] =
                         new AltLDrecv<int>(chan[1],
                                            this,
                                            (Smf2_ptr)&Sync::alt_body4,
                                            0,
                                            pid[1]);
                alt_table4[2] =
                        new AltLDrecv<int>(chan[2],
                                           this,
                                           (Smf2_ptr)&Sync::alt_body4,
                                           0,
                                           pid[2]);
                alt_table4[3] =
                         new AltLDrecv<int>(chan[3],
                                            this,
                                            (Smf2_ptr)&Sync::alt_body4,
                                            0,
                                            pid[3]);

                AltCtrl *alt_ctrl4 = new AltCtrl(4, alt_table4);
                for(int i = 0; i< 4; i++){
                   Sdf::alt(alt_ctrl4);
                }

                fprintf(Sdf::trace, "node %d, sync after alt\n", Sdf::PROC_ID);
                fflush(Sdf::trace);

                delete alt_table4[0],  delete alt_table4[1], 
                delete alt_table4[2],  delete alt_table4[3];
                delete alt_ctrl4;
        }


        void alt_body2(int rank, void *arg_ptr){
               //notice the first implicit parameter

             if(rank==0)
                 fprintf(Sdf::trace, "node %d, alt_body2.%d, id_left.processor_Id = %d, id_left.process_Id = %d\n",  
                 Sdf::PROC_ID, rank, id_left.processor_Id, id_left.process_Id);

            if(rank==1)
                 fprintf(Sdf::trace, "node %, alt_body2.%d, id_right.processor_Id = %d, id_right.process_Id = %d\n", 
                 Sdf::PROC_ID, rank, id_left.processor_Id, id_left.process_Id);
            fflush(Sdf::trace);
        }



        void alt_body4(int rank, void *arg_ptr){
            //notice the first implicit parameter

            fprintf(Sdf::trace, "node %d, alt_proc%d, pid[%d] = %d, &pid[%d] = %d\n",
                    Sdf::PROC_ID, rank, rank, pid[rank], rank, &pid[rank]);
            fflush(Sdf::trace);
        }
};

class Root_proc: public Thread{

   public:
        Root_proc()
                : Thread((Tmf0_ptr)&Root_proc::body){}

        void body(){

                fprintf(Sdf::trace, "node %d, in Root_proc::body1, this_thread=%d\n", 
                        Sdf::PROC_ID, Sdf::this_thread);
                fflush(Sdf::trace);
                int left_id = ((Sdf::PROC_ID > 0)? Sdf::PROC_ID-1 : Sdf::NOPID);
                proc_to_left = new ChanRD<Id>(gid1, left_id);
                   //synchronous
                int right_id = ((Sdf::PROC_ID < Sdf::PROC_NB-1)? Sdf::PROC_ID+1 : Sdf::NOPID);
                proc_to_right = new ChanRD<Id>(gid2, right_id);
                   //synchronous

               Sdf::barrier(cid);
                //synchronizes chan construction(no longer
                //necessary starting from version 5)

                chan[0]         = new ChanLD<int>;
                chan[1]         = new ChanLD<int>;
                chan[2]         = new ChanLD<int>;
                chan[3]         = new ChanLD<int>;

                Proc *proc1 = new Proc(1, 0);
                Proc *proc2 = new Proc(2, 0);
                Sync *sync  = new Sync;

                fprintf(Sdf::trace, "node %d, in Root_proc::body2\n", Sdf::PROC_ID);
                fflush(Sdf::trace);

                S_Thread *proc_tabl[3] = {proc1, proc2, sync};
                int pri[3] = {1, 1, 1};
                par(3, proc_tabl, pri);

                fprintf(Sdf::trace, "node %d, in Root_proc::body4, this_thread=%d\n", 
                        Sdf::PROC_ID, Sdf::this_thread);
                fflush(Sdf::trace);

#if TRACE_FILE == 2
//These functions are library internal ones,
//they are only allowed in library test mode
//in this case, the trace is generated in the file ARCH_trace.
Sched_intern::sched_data->sched_q_print(1);
Sched_intern::sched_data->embryo_q_print();
Sched_intern::sched_data->threadctrl_q_print();
#endif

                fprintf(Sdf::trace, "node %d, in Root_proc::body5, this_thread=%d\n", 
                        Sdf::PROC_ID, Sdf::this_thread);
                fflush(Sdf::trace);

                Sdf::barrier(cid);
                     //synchronizes chan destruction
                delete chan[0],  delete chan[1], 
                delete chan[2],  delete chan[3];
                delete proc1, delete proc2, delete sync;
                delete proc_to_left, delete proc_to_right;

#if TRACE_FILE == 2
//These functions are library internal ones,
//they are only allowed in library test mode
//in this case, the trace is generated in the file ARCH_trace.
Sched_intern::sched_data->sched_q_print(1);
Sched_intern::sched_data->embryo_q_print();
Sched_intern::sched_data->threadctrl_q_print();
#endif
        }
};

//end of user program


MAIN(Root_proc, init_gid, Sdf::empty, Sdf::empty)


